﻿package de.gameduell.framework.game.billard.view {
	import sandy.core.Scene3D;
	import sandy.core.scenegraph.Camera3D;
	import sandy.core.scenegraph.Group;
	import sandy.materials.Appearance;
	import sandy.materials.BitmapMaterial;
	import sandy.materials.Material;
	import sandy.primitive.Sphere;
	
	import de.gameduell.framework.debug.Debug;
	import de.gameduell.framework.game.billard.controller.AnimationController;
	import de.gameduell.framework.game.billard.controller.IBillardController;
	import de.gameduell.framework.game.billard.controller.PhysicController;
	import de.gameduell.framework.game.billard.event.AnimationEvent;
	import de.gameduell.framework.game.billard.event.PhysicEvent;
	import de.gameduell.framework.game.billard.model.Ball;
	import de.gameduell.framework.game.billard.model.BillardSettings;
	import de.gameduell.framework.geom.Geometry;
	import de.gameduell.framework.util.Audio;
	import de.gameduell.framework.util.Library;
//	import de.gameduell.game.mpp.src.controller.Controller;
	
	import org.cove.ape.Quaternion;
	import org.cove.ape.Vector;
	
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.filters.DropShadowFilter;	

	/**
	 * @author cbra
	 */
	public class BallView {	
		private static const SHADOW_SCALE:Number = 0.03;
		private static const CAM_DISTANCE_FACTOR:int = 100000;
		
		protected var world3D:Scene3D;
		protected var scene3D:Group;
		protected var ballGroup:Group;
		protected var library:Library;
		protected var camera:Camera3D;
				
		protected var isWhiteBall:Boolean;
		protected var ballModel:Sphere;
		protected var id:String;
		protected var ballSprite:Sprite;
		protected var shadowSprite:Sprite;
		protected var dropShadow:DropShadowFilter;
		protected var lightPos:Vector;
		protected var highlightSprite:Sprite;
		protected var selfShadowSprite:Sprite;
		protected var inHoleContainer:Sprite;
		
		protected var fallen:Boolean;
		protected var darken:Sprite;
		protected var content:Sprite;
		
		protected var ball:Ball;
		protected var animationController:AnimationController;	
		protected var physicController:PhysicController;
		protected var controller:IBillardController;
		protected var audio:Audio;
		protected var billardSettings:BillardSettings;
		
		protected var headSpotPosition:Vector;
		protected var ballInHandArrows:MovieClip;
		protected var tableBounds:Sprite;
		protected var lastValidposition:Vector = null;

		
		public function BallView(index:int, aContent:Sprite, aController:IBillardController) {
			library			    = Library.getInstance();	
			audio 			    = Audio.getInstance();
			
			controller 		    = aController;	
			physicController    = controller.physicController;	
			billardSettings     = controller.billardSettings;		
			animationController = controller.animationController;
		
			physicController.addEventListener(PhysicEvent.BALL_MOVED, onBallMoved);
			physicController.addEventListener(PhysicEvent.BALL_RESET, reset);
//			physicController.addEventListener(PhysicEvent.CALCULATION_START, ballInHandOver);
			animationController.addEventListener(AnimationEvent.ANIMATION_STEP, update);
			
			content 		 = aContent;
			ball 	 		 = physicController.balls[index];
			
			id		 		 = "ball_"+index;
			isWhiteBall 	 = id == "ball_0";
				
			tableBounds		 = content.getChildByName("tableBounds") as Sprite;	
			ballSprite   	 = content.getChildByName(id) as Sprite;			
			shadowSprite 	 = content.getChildByName(id+"_shadow") as Sprite;
			highlightSprite  = ballSprite.getChildByName("highlight") as Sprite;
			selfShadowSprite = ballSprite.getChildByName("selfShadow") as Sprite;
			inHoleContainer  = content.getChildByName("inHoleBalls") as Sprite;
			var lightSprite:Sprite = content.getChildByName("lightPos") as Sprite;
			lightPos = new Vector(lightSprite.x,lightSprite.y);
			dropShadow = DropShadowFilter(shadowSprite.filters[0]);
			darken = ballSprite.getChildByName("darken") as Sprite;
			fallen = false;
			createWorld();
			
			update();
			update3DModel(ball.rotation);
//			checkDragMode();
		}

		protected function reset(event:PhysicEvent):void {
			Debug.trace("BallView:reset");
			animationController.addEventListener(AnimationEvent.ANIMATION_STEP, update);
			if(ballSprite.mask != null) {
				content.removeChild(ballSprite.mask);
				ballSprite.mask = null;
			}
			ballSprite.visible = true;
			shadowSprite.visible = true;
			darken.alpha = 0;	
			fallen = false;
			updatePosition(ball.position);		
		}
		
		protected function moveToLastValidPosition():void{
			if(isWhiteBall){
				physicController.moveBall(ball.id,lastValidposition.clone());
				ballInHandArrows.x = lastValidposition.x;
				ballInHandArrows.y = lastValidposition.y;
			}
		}
		
		protected function startDrag(event:MouseEvent):void {	
			if(ballSprite.hitTestPoint(content.mouseX, content.mouseY) ) {
				content.stage.addEventListener(MouseEvent.MOUSE_MOVE, onDrag);
				content.stage.addEventListener(MouseEvent.MOUSE_UP, stopDrag);
			}
		}

		protected function stopDrag(event:MouseEvent):void {		
			content.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDrag);
			content.stage.removeEventListener(MouseEvent.MOUSE_UP, stopDrag);
			moveToLastValidPosition();
			physicController.placedBall();
		}
		
		protected function onDrag(event:MouseEvent):void {
			if(tableBounds.hitTestPoint(content.mouseX, content.mouseY)){
				physicController.moveBall(ball.id,new Vector(content.mouseX, content.mouseY));
				if(isWhiteBall){
					if(physicController.whiteBallPositionValid()){
						lastValidposition = ball.position.clone();
					}
					ballInHandArrows.x = content.mouseX;
					ballInHandArrows.y = content.mouseY;
				}			
			}
		}
		
		protected function onBallMoved(event:Event):void{
			updateLight(ball.position);		
			updatePosition(ball.position);	
		}

		private function update(event:Event = null):void {	
			if(ball.step.removed) {
				removeBall();
			}else {			
				if(!ball.step.isStopped || event == null) {	
					ballInPocket();		
					updateLight(ball.step.position);		
					updatePosition(ball.step.position);	
					update3DModel(ball.step.rotation);				
				}			
			}
		}
		
		private function removeBall():void {
			Debug.trace("BallView:update ball removed " + ball.id);
			animationController.removeEventListener(AnimationEvent.ANIMATION_STEP, update);
			ballSprite.visible = false;
			shadowSprite.visible = false;
		}

		private function update3DModel(rotation:Quaternion):void{
			rotation.toSandyRotationMatrix(ballModel.matrix);
			world3D.render();	
		}
		
		private function updatePosition(position:Vector):void {
			ballSprite.x = position.x;
			ballSprite.y = position.y;
			shadowSprite.x = position.x;
			shadowSprite.y = position.y;
		}

		// light and shadow on the ball
		private function updateLight(position:Vector):void {
			var lightDistance:Vector = position.minus(lightPos);
			var shadowDistance:Number = SHADOW_SCALE * lightDistance.magnitude();
			var angle:Number = 180 + Geometry.arcToAngle(-Math.atan2(lightDistance.y, -lightDistance.x));	
			dropShadow.angle = angle;	
			dropShadow.distance = shadowDistance;
			dropShadow.strength = Math.min(1, 3 / (0.000001 + shadowDistance));
			shadowSprite.filters = new Array(dropShadow);				
			highlightSprite.rotation = angle + 180;
			selfShadowSprite.rotation = angle + 180;
		}

		// animates the ball when it is in a pocket
		private function ballInPocket():void {
			// mask the ballSprite when it falls into a pocket
			if(ball.step.fallen && !fallen) {
				Debug.trace("ballInPocket");
				fallen = true;
				var maskSprite:Sprite = library.createSprite("holeDummy");
				content.addChild(maskSprite);
				maskSprite.x = ball.step.hole.sprite.x;
				maskSprite.y = ball.step.hole.sprite.y;
				var mask:Sprite = maskSprite;
				ballSprite.mask = mask;
				shadowSprite.visible = false;
				audio.playSound("ball_pocketed");
			}
			// darken the ball when it falls into a pocket
			if(!(ball.step.hole == null)) {
				if(darken.alpha < 0.6) {
					darken.alpha += 0.05;
				}
			}
		}
		
		// creates the 3D world of the ballModel
		private function createWorld():void{		
			world3D  = new Scene3D(null, null, null, null );
			world3D.container = ballSprite.getChildByName("container") as Sprite;
			camera =  new Camera3D(260,260,45/CAM_DISTANCE_FACTOR,50,100000000);
 			world3D.camera = camera;
			scene3D = new Group("scene");
			scene3D.addChild(world3D.camera);
			ballGroup = new Group("balls");
			scene3D.addChild(ballGroup);
			world3D.root = scene3D;	
			
			ballModel = createBall(10,10);
				
			ballGroup.addChild(ballModel);	
			camera.z = 70 * CAM_DISTANCE_FACTOR;					
			camera.lookAt(ballModel.x, ballModel.y, ballModel.z);		
		}
		
		// creates the 3D representation of the ball
		private function createBall(wSegments:int,hSegments:int):Sphere{	
			var ballBitmapData:BitmapData = library.createBitmapData(id, 1, 1);
			var material:Material = new BitmapMaterial(ballBitmapData);
			
			var newBallall:Sphere = new Sphere(id,11,wSegments,hSegments);
			newBallall.useSingleContainer = true;		
			newBallall.container.x = -130;
			newBallall.container.y = -130;
			newBallall.appearance = new Appearance( material);	
			return newBallall;				
		}	
		
		
		

	}
	
}
	

